//////////////////////////////////////////////////////////////////////
//
// NO_IArchive.cpp interface for the NO_IArchive class.
//
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
#define NO_TAL_EXPORT

//////////////////////////////////////////////////////////////////////////
#include "NO_Externs.h"
#include "NO_IArchive.h"

//////////////////////////////////////////////////////////////////////////
const NO_Type* type = 0;

//////////////////////////////////////////////////////////////////////////
// con/destruction
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
NO_IArchive::NO_IArchive(void):typeID(0),num(0),ptrID(0),pointerFound(0){}

//////////////////////////////////////////////////////////////////////////
// use
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
NO_IArchive& NO_IArchive::operator>>(NO_Object& storage)
{
	if(!stream)	{return *this;}
	pointerFound = 0;
	peekBegin();
	NO_Object object(NO_TypeID<>::type(typeID),num);

	try{range(object);}
	catch (std::bad_cast& e)	{std::cout<<e.what()<<std::endl;}
	catch (std::bad_typeid& e)	{std::cout<<e.what()<<std::endl;}
	catch (std::exception& e)	{std::cout<<e.what()<<std::endl;}
	catch (...)					{std::cout<<"Stream failed"<<std::endl;}
	
	storage = object;
	return *this;
}

//////////////////////////////////////////////////////////////////////////
// use
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
NO_IArchive& NO_IArchive::objectBegin(NO_Object& object)
{
	if(!stream){throw std::exception("NO_IArchive: stream empty");}
	readBegin();
	registerObject(object,ptrID);
	return *this;
}

//////////////////////////////////////////////////////////////////////////
NO_IArchive& NO_IArchive::objectEnd(NO_Object& object)
{
	if(!stream){throw std::exception("NO_IArchive: stream empty");}
	readEnd();
	return *this;
}

//////////////////////////////////////////////////////////////////////////
NO_IArchive& NO_IArchive::range(NO_Object& object)
{
	if(!stream){throw std::exception("NO_IArchive: stream empty");}

	peekBegin();
	if(!ptrID){return readBegin().readEnd();}
	
	NO_Object found = findObject(ptrID);
	if(found&&found.getType().getID()==typeID)
	{
		if(object.getType().getTypeCode()==NO_OBJECT_TYPE)
		{
			NO_Object& o = *noGetDataException<NO_Object>(object.cast(NO_TypeID<NO_Object>::type()));		
			o = found;
		}
		if(!doReferences){return readBegin().readEnd();}
	}

	if(!object)	{object = restore();}
	if(num==1)	{return object.getType().input(object,*this);}

	NO_UInt32 tn = num;
	objectBegin(object);
	for(NO_UInt32 i = 0; i<tn; i++)
	{
		NO_Object element = object.element(i);
		element.getType().input(element, *this);
	}
	objectEnd(object);

	return *this;
}

//////////////////////////////////////////////////////////////////////
NO_IArchive& NO_IArchive::bases(NO_Object& object)
{
	if(!stream){throw std::exception("NO_IArchive: stream empty");}
	if(!object){throw std::exception("NO_IArchive: object empty");}
	
	const NO_Type& type = object.getType();
	const NO_IBase* bIter = type.beginBase();
	const NO_IBase* bEnd = type.endBase();
	while (bIter!=bEnd)
	{
		NO_Object base(object.base(*bIter));
		members(base);
		bIter = type.nextBase(bIter);
	}
	
	return *this;
}	

//////////////////////////////////////////////////////////////////////
NO_IArchive& NO_IArchive::members(NO_Object& object)
{
	if(!stream){throw std::exception("NO_IArchive: stream empty");}
	if(!object){throw std::exception("NO_IArchive: object empty");}
		
	const NO_Type& type = object.getType();
	const NO_IMember * iter = type.beginMember();
	const NO_IMember * end = type.endMember();
	while (iter!=end)
	{
		NO_Object element = object.member(*(iter));
		range(element);
		iter = type.nextMember(iter);
	}
	
	return *this;
}

//////////////////////////////////////////////////////////////////////////
// protected/private
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
void NO_IArchive::setLastRead(NO_UInt32 t, NO_UInt32 p, NO_UInt32 n)
{
	typeID = t;
	ptrID = p;
	num = n;
}

//////////////////////////////////////////////////////////////////////////
void NO_IArchive::peekBegin(void)
{
	if(!stream){throw std::exception("NO_IArchive: stream empty");}
	
	NO_UInt32 pos = stream.tellg();
	readBegin();
	stream.seekg(pos);
}

//////////////////////////////////////////////////////////////////////////
NO_Object NO_IArchive::restore(void) 
{
	peekBegin();
	NO_Object found = findObject(ptrID);
	if (found){return found;}
	return NO_TypeID<>::type(typeID).getReplace(num);
}

//////////////////////////////////////////////////////////////////////////
NO_Boolean	NO_IArchive::verify (NO_Object& object)
{
	peekBegin();
	NO_Boolean is = object;
	if(!is&&ptrID==0)					{return 1;}
	if(!is&&ptrID!=0)					{return 0;}
	if(object.getNum()!=num)			{return 0;}
	if(object.getType().getID()!=typeID){return 0;}

	return 1;
}
